package com.example.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.example.common.Constants;
import com.example.common.ResultCode;
import com.example.common.RoleEnum;
import com.example.entity.Account;
import com.example.entity.DiskFiles;
import com.example.exception.CustomException;
import com.example.mapper.DiskFilesMapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;

/**
 * 件系统核心服务
 * 功能：文件上传下载、目录管理、文件预览等
 */
@Service
public class DiskFilesService {


    private static final String filePath = "D:/AAA/files/";

    private static final Logger log = LoggerFactory.getLogger(DiskFilesService.class);

    @Value("${server.port:8888}")
    private String port;

    @Value("${ip:localhost}")
    private String ip;

    @Resource
    private DiskFilesMapper diskFilesMapper;


    /**
     * 文件/文件夹上传核心方法
     * @param file 上传的文件
     * @param name 文件/文件夹名称
     * @param folder 文件夹标识（Constants.IS_FOLDER / Constants.NOT_FOLDER）
     * @param folderId 所属父目录ID
     * @param request HTTP请求对象（用于获取当前用户）
     */
    public void add(MultipartFile file, String name, String folder, Integer folderId,HttpServletRequest request) {
        // 参数传  文件夹的名称 是否是文件夹  文件夹的ID
        DiskFiles diskFiles = new DiskFiles();
        String now = DateUtil.now();
        diskFiles.setCrateTime(now);
        diskFiles.setUpdateTime(now);
        diskFiles.setFolder(folder);
        diskFiles.setName(name);
        diskFiles.setType(Constants.FILE_TYPE_FOLDER);  // 默认是文件夹  后面如果是文件的话 会覆盖这个值
        Account account = (Account) request.getSession().getAttribute("user");
        if (account != null) {
         diskFiles.setUserId(account.getId().intValue());
        }
        diskFiles.setUserId(1);
        diskFiles.setFolderId(folderId); // 当前文件/文件夹 外传的目录ID
        if (Constants.NOT_FOLDER.equals(folder)) {   // 文件操作
            String originalFilename = file.getOriginalFilename();
            diskFiles.setName(originalFilename);
            String extName = FileUtil.extName(originalFilename);   // 获取文件的后缀
            diskFiles.setType(extName);
            long flag = System.currentTimeMillis();
            String fileName = flag + "-" + originalFilename;  // 文件存储在磁盘的文件名
            if (!FileUtil.exist(filePath)) {
                FileUtil.mkdir(filePath);
            }
            try {
                byte[] bytes = file.getBytes();  // byte
                double size = BigDecimal.valueOf(bytes.length).divide(BigDecimal.valueOf(1024), 3, RoundingMode.HALF_UP).doubleValue();
                diskFiles.setSize(size);
                // 文件上传
                file.transferTo(new File(filePath + fileName));
            } catch (Exception e) {
                log.error("文件上传错误", e);
            }
            String url = "http://" + ip + ":" + port + "/diskFiles/download/" + fileName;
            diskFiles.setFile(url);
        }
        diskFilesMapper.insert(diskFiles);


        if (folderId != null) {  // 外层有目录
            DiskFiles parentFolder = this.selectById(folderId);  // 获取到外层的目录
            Integer rootFolderId = parentFolder.getRootFolderId();
            diskFiles.setRootFolderId(rootFolderId);
        } else {
            if (Constants.IS_FOLDER.equals(folder)) {  // 当前是新建目录操作
                Integer diskFilesId = diskFiles.getId();  // 刚才插入到数据库的文件的ID
                diskFiles.setRootFolderId(diskFilesId);
            }
        }
        this.updateById(diskFiles);  // 更新 root_folder_id 字段的值
    }

    /**
     * 文件下载方法
     * @param flag 文件唯一标识（即存储时的文件名）
     * @param response HTTP响应对象
     */
    public void download(String flag, HttpServletResponse response) {
        OutputStream os;
        try {
            if (StrUtil.isNotEmpty(flag)) {
                response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(flag, "UTF-8"));
                response.setContentType("application/octet-stream");
                byte[] bytes = FileUtil.readBytes(filePath + flag);
                os = response.getOutputStream();
                os.write(bytes);
                os.flush();
                os.close();
            }
        } catch (Exception e) {
            System.out.println("文件下载失败");
        }
    }

    /**
     * 文件预览方法（支持图片/PDF等）
     * @param id 文件数据库ID
     * @param response HTTP响应对象
     */
    public void preview(Integer id, HttpServletResponse response) {
        DiskFiles diskFiles = this.selectById(id);
        if (diskFiles == null) {
            throw new CustomException(ResultCode.FILE_NOT_FOUND);
        }
        ArrayList<String> typeList = CollUtil.newArrayList("jpg", "jpeg", "png", "pdf", "gif");
        if (!typeList.contains(diskFiles.getType())) {
            throw new CustomException(ResultCode.TYPE_NOT_SUPPORT);
        }
        OutputStream os;
        try {
            String file = diskFiles.getFile();
            String flag = file.substring(file.lastIndexOf("/"));
            response.addHeader("Content-Disposition", "inline;filename=" + URLEncoder.encode(diskFiles.getName(), "UTF-8"));
            byte[] bytes = FileUtil.readBytes(filePath + flag);
            os = response.getOutputStream();
            os.write(bytes);
            os.flush();
            os.close();
        } catch (Exception e) {
            System.out.println("文件下载失败");
        }
    }



    /**
     * 根据ID删除文件/文件夹
     * @param id 文件数据库ID
     */
    public void deleteById(Integer id) {
        diskFilesMapper.deleteById(id);
    }


    /**
     * 批量删除文件/文件夹
     *  @param ids 文件数据库ID集合
     */
    public void deleteBatch(List<Integer> ids) {
        for (Integer id : ids) {
            this.deleteById(id);
        }
    }

    /**
     * 更新文件/文件夹信息
     * @param diskFiles    查询条件对象，包含文件名、文件类型、上传时间等过滤条件（可为null）
     */
    public void updateById(DiskFiles diskFiles) {
        diskFilesMapper.updateById(diskFiles);
    }

    /**
     * 根据ID查询文件详情
     * @param id 文件数据库ID
     */
    public DiskFiles selectById(Integer id) {
        return diskFilesMapper.selectById(id);
    }

    /**
     * 条件查询当前用户的所有文件（带权限控制）
     *
     * @param diskFiles    查询条件对象，可包含：
     *                     - fileName 文件名（模糊匹配）
     *                     - fileType 文件类型
     *                     - uploadTimeRange 上传时间范围
     *                     - 其他文件属性过滤条件
     * @param request      HTTP请求对象，用于获取当前登录用户信息
     * @return List<DiskFiles> 符合条件的文件列表（按创建时间倒序排列）
     *         当无匹配数据时返回空列表（永不返回null）
     *
     */
    public List<DiskFiles> selectAll(DiskFiles diskFiles,HttpServletRequest request) {
        Account account = (Account) request.getSession().getAttribute("user");
        if (account != null) {
            diskFiles.setUserId(account.getId().intValue());
        }
        return diskFilesMapper.selectAll(diskFiles);
    }

    /**
     * 分页查询文件列表
     * @param diskFiles    查询条件对象，包含文件名、文件类型、上传时间等过滤条件（可为null）
     * @param pageNum      当前页码（从1开始），若为null则使用默认值
     * @param pageSize     每页显示记录数，若为null则使用默认值
     * @return PageInfo<DiskFiles> 分页结果包装对象，包含：
     *         - 当前页数据列表（List<DiskFiles>）
     *         - 总记录数（total）
     *         - 总页数（pages）
     *         - 当前页码（pageNum）
     *         - 每页大小（pageSize）
     *         - 是否有前一页/下一页等导航信息
     */
    public PageInfo<DiskFiles> selectPage(DiskFiles diskFiles, Integer pageNum, Integer pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        List<DiskFiles> list = diskFilesMapper.selectAllData(diskFiles);
        return PageInfo.of(list);
    }

    /**
     * 递归查询文件夹路径层级（从指定文件夹向上追溯到根目录）
     *
     * @param folderId   当前文件夹ID（当为null时返回空列表）
     * @param list       结果收集列表
     * @return List<DiskFiles> 文件夹路径列表（按从子到父的顺序排列）
     */
    public List<DiskFiles> selectFolderNames(Integer folderId, List<DiskFiles> list) {
        DiskFiles diskFiles = this.selectById(folderId);
        if (diskFiles == null) {
            return list;
        }
        list.add(diskFiles);  // 把当前节点的名称加到list里面去  最后一起返回
        Integer parentFolderId = diskFiles.getFolderId(); // 父级ID
        if (parentFolderId == null) {  // 当前目录的外层没有目录  就结束
            return list;
        }
        return this.selectFolderNames(parentFolderId, list);
    }

    /**
     * 统计指定天数范围内的每日文件数量
     *
     * @param days 统计天数范围（包含今天之前days天的数据，例如days=7统计最近7天）
     * @return List<Dict> 统计结果列表，每个元素包含：
     *         - date: 日期字符串（格式：yyyy-MM-dd）
     *         - count: 当天文件数量
     */
    public List<Dict> count(Integer days) {
        List<Dict> list = new ArrayList<>();
        Date now = new Date();
        DateTime end = DateUtil.offsetDay(now, -1);// 前一天  -1
        DateTime start = DateUtil.offsetDay(now, -days);// 前 days天
        List<DateTime> dateTimeList = DateUtil.rangeToList(start, end, DateField.DAY_OF_YEAR);
        List<String> dateList = dateTimeList.stream().map(DateUtil::formatDate).sorted(String::compareTo).collect(Collectors.toList());
        for (String date : dateList) {
            Integer count = diskFilesMapper.selectByDate(date);
            Dict dict = Dict.create().set("date", date).set("count", count);
            list.add(dict);
        }
        return list;
    }

    /**
     * 构建用户文件树形结构（支持多级文件夹嵌套）
     *
     * @param userId 用户ID（用于过滤可见文件）
     * @return List<DiskFiles> 树形结构根节点列表（每个节点包含label和children）
     *
     * @implNote 实现细节：
     * 1. 首先查询用户的所有根文件夹（findRootFolders）
     * 2. 对每个根节点递归构建子树（通过buildTree私有方法）
     * 3. 文件夹类型(folder)会递归查询子节点，文件类型(file)不处理children
     *
     * @example 返回数据结构：
     * [
     *   {
     *     "id": 1,
     *     "label": "根文件夹",
     *     "children": [
     *       {
     *         "id": 2,
     *         "label": "子文件夹",
     *         "children": [...]
     *       }
     *     ]
     *   }
     * ]
     *
     */
    public List<DiskFiles> getFileTree(Integer userId) {
        // 获取所有根节点
        List<DiskFiles> rootFiles = diskFilesMapper.findRootFolders(userId);
        System.out.println("userId=="+userId);
        return buildTree(rootFiles, userId);
    }

    /**
     * 递归构建文件树节点（私有辅助方法）
     *
     * @param files   当前层级的文件列表
     * @param userId  用户ID（用于权限过滤）
     * @return List<DiskFiles> 构建好的树节点列表
     *
     * @implNote 实现细节：
     * 1. 使用Stream API处理集合转换
     * 2. 每个节点只保留id和label字段
     * 3. 文件夹类型会递归查询子节点并构建嵌套结构
     */
    private List<DiskFiles> buildTree(List<DiskFiles> files, Integer userId) {
        return files.stream().map(file -> {
            DiskFiles node = new DiskFiles();
            node.setId(file.getId());
            node.setLabel(file.getName());

            // 如果是文件夹，递归查询子节点
            if ("folder".equals(file.getType())) {
                List<DiskFiles> children = diskFilesMapper.findChildren(file.getId(), userId);
                node.setChildren(buildTree(children, userId));
            }
            return node;
        }).collect(Collectors.toList());

    }
}